﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;

namespace mvcForum.Tests.WebUI {

	public static class ForumTestHelper {

		public static void GoToForum(this IWebDriver driver, String name) {
			driver.VisitURL("forum");

			IWebElement forumLink = driver.FindElement(By.LinkText(name));
			forumLink.Click();

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.FindElement(By.Id("forum_forum_index")));
		}

		public static void GoToSubForum(this IWebDriver driver, String forumName, String subForumName) {
			driver.VisitURL("forum");

			IWebElement forumLink = driver.FindElement(By.LinkText(forumName));
			forumLink.Click();

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.FindElement(By.Id("forum_forum_index")));

			forumLink = driver.FindElement(By.LinkText(subForumName));
			forumLink.Click();

			wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.FindElement(By.Id("forum_forum_index")));
		}

		public static void GoToTopic(this IWebDriver driver, String forumName, String title) {
			driver.GoToForum(forumName);

			IWebElement topicLink = driver.FindElement(By.LinkText(title));
			topicLink.Click();

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.FindElement(By.Id("forum_topic_index")));
		}

		public static void CreateUser(this IWebDriver driver, String username, String password, String email) {
			driver.VisitURL("forum/account/register");

			IWebElement usernameBox = driver.FindElement(By.Name("Username"));
			usernameBox.SendKeys(username);

			IWebElement emailBox = driver.FindElement(By.Name("EmailAddress"));
			emailBox.SendKeys(email);

			IWebElement passwordBox = driver.FindElement(By.Name("Password"));
			passwordBox.SendKeys(password);

			IWebElement confirmBox = driver.FindElement(By.Name("ConfirmPassword"));
			confirmBox.SendKeys(password);

			IWebElement button = driver.FindElement(By.Name("post"));
			button.Click();
		}

		public static Boolean LogOn(this IWebDriver driver, String email, String password) {
			driver.VisitURL("forum/account/logon");

			IWebElement emailBox = driver.FindElement(By.Name("EmailAddress"));
			emailBox.SendKeys(email);

			IWebElement passwordBox = driver.FindElement(By.Name("Password"));
			passwordBox.SendKeys(password);

			IWebElement button = driver.FindElement(By.ClassName("default-submit-action"));
			button.Click();

			return String.Format("{0}forum", TestConfiguration.SiteBaseURL) == driver.Url.ToLower();
		}

		public static void LogOff(this IWebDriver driver) {
			driver.VisitURL("forum/account/logoff");
			if (driver.Manage().Cookies.GetCookieNamed(".ASPXAUTH") != null) {
				driver.Manage().Cookies.DeleteCookieNamed(".ASPXAUTH");
			}
		}

		public static IWebElement FindForumLI(this IWebDriver driver, String forumName) {
			IWebElement forumRow = null;
			ReadOnlyCollection<IWebElement> lists = driver.FindElement(By.Id("middle5")).FindElements(By.XPath("div/div/div/ul"));
			foreach (IWebElement list in lists) {
				if (list.GetAttribute("class").Contains("forums")) {
					ReadOnlyCollection<IWebElement> forums = list.FindElements(By.XPath("li"));
					foreach (IWebElement forum in forums) {
						IWebElement name = forum.FindElement(By.XPath("dl/dt/a"));
						if (name.Text == forumName) {
							forumRow = forum;
							break;
						}
					}
				}
				if (forumRow != null) {
					break;
				}
			}
			return forumRow;
		}

		public static void GetForumInfo(this IWebDriver driver, String forumName, out Int32 topics, out Int32 posts, out String latestTopicTitle, out String latestAuthor) {
			IWebElement forumRow = driver.FindForumLI(forumName);
			if (forumRow == null) {
				throw new ApplicationException("Forum not found");
			}

			topics = 0;
			posts = 0;
			latestAuthor = String.Empty;
			latestTopicTitle = String.Empty;

			ReadOnlyCollection<IWebElement> cells = forumRow.FindElements(By.XPath("dl/dd"));
			foreach (IWebElement cell in cells) {
				if (cell.GetAttribute("class") == "topics") {
					String temp = String.Empty;
					for (Int32 index = 0; index < cell.Text.Length; index++) {
						if (!Char.IsDigit(cell.Text[index])) {
							break;
						}
						temp += cell.Text[index];
					}
					Int32.TryParse(temp, out topics);
				}
				else if (cell.GetAttribute("class") == "posts") {
					String temp = String.Empty;
					for (Int32 index = 0; index < cell.Text.Length; index++) {
						if (!Char.IsDigit(cell.Text[index])) {
							break;
						}
						temp += cell.Text[index];
					}
					Int32.TryParse(temp, out posts);
				}
				else if (cell.GetAttribute("class") == "lastpost") {
					ReadOnlyCollection<IWebElement> links = cell.FindElements(By.XPath("span/a"));
					latestAuthor = links[0].Text;
					latestTopicTitle = links[1].Text;
				}
			}
		}

		public static void CreateTopic(this IWebDriver driver, String forumName, String type, String title, String content) {
			driver.GoToForum(forumName);

			IWebElement link = driver.FindElement(By.LinkText("Post a new topic"));
			link.Click();
			driver.Wait(1);

			driver.InsertText("Subject", title);
			driver.InsertText("Body", content);

			if (!String.IsNullOrEmpty(type)) {
				IWebElement typeDropDown = driver.FindElement(By.Name("Type"));
				foreach (IWebElement option in typeDropDown.FindElements(By.TagName("option"))) {
					if (option.Text == type) {
						option.Click();
						break;
					}
				}
			}

			IWebElement button = driver.FindElement(By.Name("post"));
			button.Click();
		}

		public static void CreateTopic(this IWebDriver driver, String forumName, String title, String content) {
			driver.CreateTopic(forumName, String.Empty, title, content);
		}

		public static void CreateSubForumTopic(this IWebDriver driver, String forumName, String subForumName, String title, String content) {
			driver.CreateSubForumTopic(forumName, subForumName, String.Empty, title, content);
		}
		public static void CreateSubForumTopic(this IWebDriver driver, String forumName, String subForumName, String type, String title, String content) {
			driver.GoToSubForum(forumName, subForumName);

			IWebElement link = driver.FindElement(By.LinkText("Post a new topic"));
			link.Click();
			driver.Wait(1);

			driver.InsertText("Subject", title);
			driver.InsertText("Body", content);

			if (!String.IsNullOrEmpty(type)) {
				IWebElement typeDropDown = driver.FindElement(By.Name("Type"));
				foreach (IWebElement option in typeDropDown.FindElements(By.TagName("option"))) {
					if (option.Text == type) {
						option.Click();
						break;
					}
				}
			}

			IWebElement button = driver.FindElement(By.Name("post"));
			button.Click();
		}

		public static void EnableSynchronouslySearch(this IWebDriver driver) {
			driver.VisitURL("forumadmin/luceneconfiguration");

			driver.UnselectCheckbox("RunAsynchronously");
			driver.SelectCheckbox("Enabled");
			driver.Submit("Save");
		}

		public static void EnableMultiPost(this IWebDriver driver, Int32 interval, Int32 posts) {
			driver.EnableMultiPost(interval, posts, false, 0);
		}

		public static void EnableMultiPost(this IWebDriver driver, Int32 interval, Int32 posts, Boolean async, Int32 delay) {
			// Go to the multi-post config page!
			driver.VisitURL("forumadmin/multipostconfiguration");

			driver.SelectCheckbox("Enabled");

			if (async) {
				driver.SelectCheckbox("RunAsynchronously");
				driver.InsertText("Delay", delay.ToString());
			}
			else {
				driver.UnselectCheckbox("RunAsynchronously");
			}

			driver.InsertText("Interval", interval.ToString());

			driver.InsertText("Posts", posts.ToString());

			driver.Submit("Save");

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.PageSource.Contains("Changes Saved"));
		}

		public static void DisableMultiPost(this IWebDriver driver) {
			// Go to the multi-post config page!
			driver.VisitURL("forumadmin/multipostconfiguration");

			driver.UnselectCheckbox("Enabled");

			driver.Submit("Save");

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.PageSource.Contains("Changes Saved"));
		}

		public static void EnableNewUserTrial(this IWebDriver driver, Int32 autoLimit, IEnumerable<String> excludeGroups) {
			driver.EnableNewUserTrial(autoLimit, excludeGroups, false, 0);
		}

		public static void EnableNewUserTrial(this IWebDriver driver, Int32 autoLimit, IEnumerable<String> excludeGroups, Boolean async, Int32 delay) {
			// Go to the multi-post config page!
			driver.VisitURL("forumadmin/newusertrialconfiguration");

			driver.SelectCheckbox("Enabled");

			if (async) {
				driver.SelectCheckbox("RunAsynchronously");
				driver.InsertText("Delay", delay.ToString());
			}
			else {
				driver.UnselectCheckbox("RunAsynchronously");
			}

			driver.InsertText("AutoLimit", autoLimit.ToString());

			// TODO: Groups!

			driver.Submit("Save");

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.PageSource.Contains("Changes Saved"));
		}

		public static void DisableNewUserTrial(this IWebDriver driver) {
			// Go to the multi-post config page!
			driver.VisitURL("forumadmin/newusertrialconfiguration");

			// Setup forum for multipost handling!!
			IWebElement enabledCheckbox = driver.FindElement(By.Name("Enabled"));
			if (enabledCheckbox.Selected) {
				enabledCheckbox.Click();
			}

			driver.Submit("Save");

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.PageSource.Contains("Changes Saved"));
		}

		public static void EnableAkismet(this IWebDriver driver, String key) {
			driver.EnableAkismet(key, false, 0);
		}

		public static void EnableAkismet(this IWebDriver driver, String key, Boolean async, Int32 delay) {
			// Go to the multi-post config page!
			driver.VisitURL("forumadmin/akismetconfiguration");

			// Setup forum for multipost handling!!
			driver.SelectCheckbox("Enabled");
			if (async) {
				driver.SelectCheckbox("RunAsynchronously");
				driver.InsertText("Delay", delay.ToString());
			}
			else {
				driver.UnselectCheckbox("RunAsynchronously");
			}

			driver.InsertText("Key", key);

			driver.SelectCheckbox("MarkAsSpamOnHit");

			driver.Submit("Save");

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.PageSource.Contains("Changes Saved"));
		}

		public static void DisableAkismet(this IWebDriver driver) {
			// Go to the multi-post config page!
			driver.VisitURL("forumadmin/akismetconfiguration");

			driver.UnselectCheckbox("Enabled");

			driver.Submit("Save");

			WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
			wait.Until(drv => drv.PageSource.Contains("Changes Saved"));
		}

		public static void CreatePost(this IWebDriver driver, String forumName, String topicTitle, String content) {
			driver.CreatePost(forumName, topicTitle, String.Empty, content);
		}

		public static void CreatePost(this IWebDriver driver, String forumName, String topicTitle, String subject, String content) {
			driver.GoToTopic(forumName, topicTitle);

			if (!String.IsNullOrEmpty(subject)) {
				driver.InsertText("Subject", subject);
			}
			driver.InsertText("Body", content);

			driver.FindElement(By.Name("post")).Click();
		}

		public static void UnquarantineTopic(this IWebDriver driver, String forumName, String topicTitle) {
			driver.LogOn(Constants.Users.Moderator.Email, Constants.Users.Moderator.Password);

			driver.GoToForum(forumName);

			driver.FindElement(By.LinkText("Moderate")).Click();
			// TODO: Wait!?

			driver.FindElement(By.LinkText(topicTitle)).Click();
			// TODO: Wait!?

			driver.FindElements(By.XPath("//a[@title='Moderate']"))[0].Click();
			// TODO: Wait!?

			driver.SelectOption("Flag", "None");
			driver.InsertText("Reason", "Testing");
			driver.Submit("Save");

			driver.LogOff();
		}

		public static void MergeTopics(this IWebDriver driver, String forumName, String topic1, String topic2) {
			driver.LogOn(Constants.Users.Moderator.Email, Constants.Users.Moderator.Password);

			driver.GoToForum(forumName);

			driver.FindElement(By.LinkText("Moderate")).Click();

			IWebElement topic1LI = driver.FindElement(By.XPath(String.Format("//li[dl/dt/@title='{0}']", topic1)));
			topic1LI.FindElement(By.XPath("dl/dd/input")).Click();

			IWebElement topic2LI = driver.FindElement(By.XPath(String.Format("//li[dl/dt/@title='{0}']", topic2)));
			topic2LI.FindElement(By.XPath("dl/dd/input")).Click();

			driver.SelectOption("action", "Merge");
			driver.Submit("Submit");

			driver.SelectCheckbox("confirm");
			driver.Submit("Merge");
		}

		public static void QuarantineTopic(this IWebDriver driver, String forumName, String topicTitle) {
			driver.LogOn(Constants.Users.Moderator.Email, Constants.Users.Moderator.Password);

			driver.GoToForum(forumName);

			driver.FindElement(By.LinkText("Moderate")).Click();
			// TODO: Wait!?

			driver.FindElement(By.LinkText(topicTitle)).Click();
			// TODO: Wait!?

			driver.FindElements(By.XPath("//a[@title='Moderate']"))[0].Click();
			// TODO: Wait!?

			driver.SelectOption("Flag", "Quarantined");
			driver.InsertText("Reason", "Testing");
			driver.Submit("Save");

			driver.LogOff();
		}

		//public static void CreateForum(this IWebDriver driver, String categoryName, String forumName) {
		//    driver.LogOn(Constants.Users.Admin.Email, Constants.Users.Admin.Password);

		//    driver.VisitURL("forumadmin");
		//    driver.FindElement(By.LinkText("Demo board")).Click();
		//    driver.FindElement(By.LinkText(categoryName)).Click();
		//    driver.FindElement(By.LinkText("[Create new forum]")).Click();

		//    driver.InsertText("Name", forumName);
		//    driver.InsertText("Description", "blah");
		//    driver.InsertText("SortOrder", "10");
		//    driver.Submit("Save");

		//    driver.FindElement(By.LinkText(forumName)).Click();

		//    driver.SelectOption("Mask1", "Admin access");
		//    driver.SelectOption("Mask2", "Readonly access");
		//    driver.SelectOption("Mask3", "Moderator access");
		//    driver.SelectOption("Mask4", "Member access");

		//    // TODO:
		//    //driver.Submit("Save");
		//    driver.FindElement(By.XPath("//form[@action='/ForumAdmin/ForumAccess/Update']/div/input[@type='submit']")).Click();

		//    driver.LogOff();
		//}

		public static void DisableAntiSpam(this IWebDriver driver) {
			driver.LogOn(Constants.Users.Admin.Email, Constants.Users.Admin.Password);

			driver.VisitURL("forumadmin/antispam");
			ReadOnlyCollection<IWebElement> links = driver.FindElements(By.XPath("//table/tbody/tr/td/a[@class='btn btn-primary']"));

			for (Int32 index = 0; index < links.Count; index++) {
				links[index].Click();
				driver.UnselectCheckbox("Enabled");
				driver.Submit("Save");

				WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, 10));
				wait.Until(drv => drv.PageSource.Contains("Changes Saved"));

				driver.VisitURL("ForumAdmin/antispam");
				links = driver.FindElements(By.XPath("//table/tbody/tr/td/a[@class='btn btn-primary']"));
			}

			driver.LogOff();
		}

		public static void EnableLucene(this IWebDriver driver, Boolean async, Int32 indexerDelay) {
			driver.LogOn(Constants.Users.Admin.Email, Constants.Users.Admin.Password);

			driver.VisitURL("forumadmin/LuceneConfiguration");
			driver.SelectCheckbox("Enabled");
			driver.InsertText("Delay", indexerDelay.ToString());
			if (async) {
				driver.SelectCheckbox("RunAsynchronously");
			}
			else {
				driver.UnselectCheckbox("RunAsynchronously");
			}

			driver.Submit("Save");

			driver.LogOff();
		}

		public static void Search(this IWebDriver driver, String query, String forum) {
			driver.Search(query, new List<String> { forum });
		}

		public static void Search(this IWebDriver driver, String query, List<String> forums) {
			driver.VisitURL("forum/search");

			IWebElement formElement = driver.FindElement(By.XPath("//form[@name='MainSearchForum']"));
			IWebElement queryBox = formElement.FindElement(By.Name("Query"));
			queryBox.Clear();
			queryBox.SendKeys(query);

			driver.InsertText("Query", query);
			driver.SelectOptions("Forums", forums);

			IWebElement submitButton = formElement.FindElement(By.XPath(".//*[@type='submit']"));
			submitButton.Click();
		}
	}
}